/**
 * TAFREX is Copyright (c) 2012 - Claritize.com, based on project TARTAR
 * @author Radek Strnad - radek.strnad@claritize.com
 * TARTAR is Copyright (c) 2004-2005 - University of Karlsruhe (UKA) & Jozef Stefan Institute (JSI).
 * @author: Aleksander Pivk
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY UKA & JSI "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UKA or JSI NOR ITS EMPLOYEES BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package com.claritize.tafrex.enumeration;

import java.util.ArrayList;
import java.util.List;

/**
 * Hierarchical representation of syntactic token categories.
 */
public enum SyntacticTokenCategory {

    TOKEN (null),
    PUNCTUATION (TOKEN),
    HTML (TOKEN),
    ALPHANUMERIC (TOKEN),
    ALPHA (ALPHANUMERIC),
    ALL_UPPERCASE (ALPHA),
    ALL_LOWERCASE (ALPHA),
    FIRST_UPPERCASE (ALPHA),
    MIXEDCASE (ALPHA),
    NUMBER (ALPHANUMERIC),
    SMALL_NUMBER (NUMBER),
    MEDIUM_NUMBER (NUMBER),
    LARGE_OR_FLOAT_NUMBER (NUMBER),
    CURRENCY (NUMBER)
    ;

    /** Parent enum. */
    private SyntacticTokenCategory parent = null;

    /** Children enums. */
    private List<SyntacticTokenCategory> children = new ArrayList<SyntacticTokenCategory>();

    /**
     * Constructor with reference to parent.
     * @param parent Parent reference
     */
    private SyntacticTokenCategory (SyntacticTokenCategory parent) {
	this.parent = parent;
	if (this.parent != null) {
	    this.parent.addChild(this);
	}
    }

    /**
     * @return parent enum, null when root
     */
    public SyntacticTokenCategory parent() {
	return parent;
    }

    /**
     * @return direct children
     */
    public SyntacticTokenCategory[] children() {
	return children.toArray(new SyntacticTokenCategory[children.size()]);
    }

    /**
     * @return all descendants
     */
    public SyntacticTokenCategory[] allDescendants() {
	List<SyntacticTokenCategory> list = new ArrayList<SyntacticTokenCategory>();
	addChildren(this, list);
	return list.toArray(new SyntacticTokenCategory[list.size()]);
    }

    /**
     * @return siblings of token
     */
    public SyntacticTokenCategory[] siblings() {
	List<SyntacticTokenCategory> list = new ArrayList<SyntacticTokenCategory>();

	if (parent() != null) {

	    list.addAll(parent.children);
	    list.remove(this);

	} else {

	    for (SyntacticTokenCategory token : SyntacticTokenCategory.values()) {
		if (token.parent() == null && token != this) {
		    list.add(token);
		}

	    }
	}

	return list.toArray(new SyntacticTokenCategory[list.size()]);
    }

    /**
     * Looksup parents and determine if <code>this</code> is a descendant
     * of <code>other</code>.
     * @param other Token to compare
     * @return true if this is descendant of other
     */
    public boolean isDescendantOf(SyntacticTokenCategory other) {
	if (other == null) {
	    return false;
	}

	for (SyntacticTokenCategory category = this;  category != null;  category = category.parent()) {
	    if (other == category) {
		return true;
	    }
	}
	return false;
    }


    /**
     * Adds recursively all children
     * @param root in tree
     * @param list to add
     */
    private static void addChildren(SyntacticTokenCategory root, List<SyntacticTokenCategory> list) {
	list.addAll(root.children);
	for (SyntacticTokenCategory child : root.children) {
	    addChildren(child, list);
	}
    }

    /**
     * Adds child to a tree.
     * @param child Child to add
     */
    private void addChild(SyntacticTokenCategory child) {
	this.children.add(child);
    }


}

